package picobot.bot;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import picobot.gui.PicoUtility;
import picobot.interfaces.core.IMap;
import picobot.interfaces.core.IPicobot;
import picobot.interfaces.core.IRule;
import picobot.rule.RuleBuilder;

/**
 * a class to represent a picobot
 */
public class Picobot implements IPicobot {

	/**
	 * the x and y coordinate of the picobot and it's state
	 */
	private int xCoordinate, yCoordinate, state;
	/**
	 * the rules of the picobot
	 */
	private List<IRule> rules = new ArrayList<IRule>();

	/**
	 * the default constructor,note: this is not the constructor for a default
	 * picobot
	 */
	public Picobot() {
		super();
	}

	/**
	 * the constructor for a default picobot: the state is 0 and the default
	 * rules are loaded
	 */
	public Picobot(String mode) {
		super();

		if (mode.equals("default")) {

			this.state = 0;

			loadRules(new RuleBuilder().parseRules(new File(new PicoUtility()
					.getDefaultRule())));
		}

	}

	@Override
	public int getXCoordinate() {
		return this.xCoordinate;
	}

	@Override
	public int getYCoordinate() {
		return this.yCoordinate;
	}

	@Override
	public int getState() {
		return this.state;
	}

	@Override
	public List<IRule> getRules() {
		return this.rules;
	}

	@Override
	public void setInitialPosition(int xCoordinate, int yCoordinate) {
		this.xCoordinate = xCoordinate;
		this.yCoordinate = yCoordinate;
	}

	@Override
	public void loadRules(List<IRule> rules) {
		this.rules = rules;
	}

	@Override
	public void apply(IRule rule) {
		try {
			// check if the state of the picobot equals the required state
			if (this.state == rule.getRequiredState()) {
				this.state = rule.getTargetState();

				// checking what the destination is:
				switch (rule.getDestination().charAt(0)) {
				// if it's north, the picobot is moved one step upwards
				case 'N':
					setInitialPosition(this.xCoordinate, this.yCoordinate + 1);
					break;
				// if it's east, the picobot is moved one step to the right
				case 'E':
					setInitialPosition(this.xCoordinate + 1, this.yCoordinate);
					break;
				// if it's west, the picobot is moved one step to the left
				case 'W':
					setInitialPosition(this.xCoordinate - 1, this.yCoordinate);
					break;
				// if it's south, the picobot is moved one step downwards
				case 'S':
					setInitialPosition(this.xCoordinate, this.yCoordinate - 1);
					break;
				// if it is supposed to stay ... well, it stays
				case 'X':
					setInitialPosition(this.xCoordinate, this.yCoordinate);
					break;
				}
			}
		} catch (RuntimeException e) {
			System.err.println("Your Rule is wrong formated!");
		}

	}

	/**
	 * Compares the equality of two strings and returns always true if the first
	 * string is "ANY". This method is used for the comparison of the condition
	 * of the requirement of a rule and a cell kind of a map. If the condition
	 * is "ANY", the cell kind doesn't matter anymore, thus true is returned
	 * 
	 * @param cond
	 *            is the condition of a rule requirement
	 * @param entry
	 *            is the cell kind in a map
	 * @return true if cond is "ANY" or cond equals entry, false otherwise
	 */
	public boolean noAnyEqual(String cond, String entry) {
		boolean val = false;

		if (cond.equals("ANY")) {
			val = true;
		} else {
			val = cond.equals(entry);
		}

		return val;
	}

	@Override
	public IRule getApplicableRule(IMap map) {
		IRule toGetRule = null;

		try {
			for (int i = 0; i < this.rules.size(); i++) {
				if ((this.state == rules.get(i).getRequiredState())
						& (noAnyEqual(rules.get(i).getConditionAt("NORTH"),
								(map.getCellKind(this.xCoordinate,
										this.yCoordinate + 1))))
						& (noAnyEqual(rules.get(i).getConditionAt("EAST"),
								(map.getCellKind(this.xCoordinate + 1,
										this.yCoordinate))))
						& (noAnyEqual(rules.get(i).getConditionAt("WEST"),
								(map.getCellKind(this.xCoordinate - 1,
										this.yCoordinate))))
						& (noAnyEqual(rules.get(i).getConditionAt("SOUTH"),
								(map.getCellKind(this.xCoordinate,
										this.yCoordinate - 1))))) {

					toGetRule = rules.get(i);
					break;
				}
			}

			// if toGetRule is still null (no applicable rule was found), an
			// exception will be thrown
			toGetRule.getTargetState();

		} catch (RuntimeException e) {
			System.err.println("No suitable rule was found! " + e.getCause());
		}

		return toGetRule;
	}

}
